package com.hy;

/**
 * Created With IntelliJ IDEA.
 * Descriptions:最长公共子序列
 *
 * 给定两个字符串 text1 和 text2，返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ，返回 0 。
 * 一个字符串的 子序列 是指这样一个新的字符串：它是由原字符串在不改变字符的相对顺序的情况下删除某些字符（也可以不删除任何字符）后组成的新字符串。
 * 例如，"ace" 是 "abcde" 的子序列，但 "aec" 不是 "abcde" 的子序列。
 * 两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
 *
 * User:Mr.Du
 * Date:2023/10/30
 * Time:21:19
 */
public class LongestCommonSubsequence {

    /**
     * 状态表示：dp[i][j]表示字符串text1的[1,i]区间和字符串text2的[1,j]区间的最长公共子序列长度（下标从1开始）
     * 状态计算：
     *  1、若text1[i] == text2[j] ，也就是说两个字符串的最后一位相等，那么问题就转化成了字符串text1的[1,j-1]区间和字符串text2的[1,j-1]区间的最长公共子序列长度再加上一，即dp[i][j] = dp[i - 1][j - 1] + 1。（下标从1开始）
     *  2、若text1[i] != text2[j] ，也就是说两个字符串的最后一位不相等，那么问题就转化成了字符串text1的[1,j-1]区间和字符串text2的[1,j-1]区间的，为什么这么说呢
     *    因为有以下三种情况，最后一种情况会被排除，因为对于case1和case2两种情况来说，最终结果都大于等于case3的结果text1[i..]>text1[i+1..]
     *    case1：text1[i]不在子序列中，如：text1: abc  text2： bc    i=0
     *    case2：text2[j]不在子序列中，如：text1: bc  text2： abc    j=0
     *    case3：text1[i]和text2[j]不在序列中，如：text1: abc  text2: dbc  i=j=0
     * 状态转移方程：
     *    case1：text1[i] == text2[j] ====> dp[i][j] = 1 + dp[i - 1][j - 1]
     *    case2：text1[i] != text2[j] ====> dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1])
     * @param text1
     * @param text2
     * @return
     */
    public int longestCommonSubsequence(String text1, String text2) {
        int m = text1.length(), n = text2.length();
        int[][] dp = new int[m + 1][n + 1];
        for(int i = 1;i < dp.length;i++){
            for(int j = 1;j < dp[0].length;j++){
                if(text1.charAt(i - 1) == text2.charAt(j - 1)){
                    dp[i][j] = 1 + dp[i - 1][j - 1];
                }else{
                    dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
                }
            }
        }
        return dp[m][n];
    }


    /**
     * 输出最长公共子序列
     * @param text1
     * @param text2
     * @return
     */
    public String longestCommonSubsequenceToString(String text1, String text2) {
        int m = text1.length(), n = text2.length();
        int[][] dp = new int[m + 1][n + 1];
        for(int i = 1;i < dp.length;i++){
            for(int j = 1;j < dp[0].length;j++){
                if(text1.charAt(i - 1) == text2.charAt(j - 1)){
                    dp[i][j] = 1 + dp[i - 1][j - 1];
                }else{
                    dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
                }
            }
        }
        int len = dp[m][n];
        char[] chars = new char[len];
        int i = m, j = n;
        while(i > 0 && j > 0){
            if(text1.charAt(i - 1) == text2.charAt(j - 1)){
                chars[len - 1] = text1.charAt(i - 1);
                i--;
                j--;
                len--;
            }else if(dp[i - 1][j] > dp[i][j - 1]){
                i--;
            }else{
                j--;
            }
        }
        return new String(chars);
    }

    public static void main(String[] args) {
        LongestCommonSubsequence longestCommonSubsequence = new LongestCommonSubsequence();
        String s = longestCommonSubsequence.longestCommonSubsequenceToString("abcdef", "ebdf");
        System.out.println(s);
    }

}
